open Ast

module StringHash = Hashtbl.Make(struct
type t = string 
let equal x y = x = y 
let hash = Hashtbl.hash 
end);;

let run (stmlist) =
let ftable = StringHash.create 50
in
 let call stmtlist stable =
    let rec eval env = function
      Noexpr -> -1, env
    | Literal(op,x) -> 
        (match op with 
	       SAdd -> x 
         | SSub -> -x), env
    | Id(op,var) ->
	let v = StringHash.mem env var in
       if v then 
	let (size,list)= StringHash.find env var in
          let value = List.hd (snd (size,list)) in 
            (match op with 
	       SAdd -> value 
             | SSub -> -value), env
       else raise (Failure ("parameter '" ^ var ^ "' not found"))  
    | ArithmeticExpression(e1, op, e2) -> 
        let v1,env = eval env e1 in
          let v2,env = eval env e2 in
            (match op with 
	           Add -> v1 + v2 
             | Sub -> v1 - v2 
             | Mul -> v1 * v2 
             | Div -> v1 / v2),env
    | ConditionalExpression(e1, op, e2) ->
        let v1,env = eval env e1 in
          let v2,env = eval env e2 in
            (match op with  
                Lt  -> if v1 <  v2 then 1 else 0
              | Leq -> if v1 <= v2 then 1 else 0
              | Gt  -> if v1 >  v2 then 1 else 0
              | Geq -> if v1 >= v2 then 1 else 0
              | Eq  -> if v1 =  v2 then 1 else 0
              | Neq -> if v1 != v2 then 1 else 0
              | And -> if v1 = 1 && v2 = 1 then 1 else 0
              | Or  -> if v1 = 1 || v2 = 1 then 1 else 0),env 
    | Increment(var) ->
    	let v = StringHash.mem env var in
        if v then 
         let (size,list)= StringHash.find env var in
	  let v1 = List.hd list in 
	   let v2 = v1 + 1 in
           (StringHash.replace env var ([],[v2])); v2, env
        else raise (Failure ("Can not call function with parameter " ^ var))   	   	
    | Decrement(var) ->             
    	let v = StringHash.mem env var in
    	if v then
	 let (size,list)= StringHash.find env var in
           let v1 = List.hd list in 
            let v2 = v1 - 1 in
           (StringHash.replace env var ([],[v2])); v2, env   	
        else raise (Failure ("Can not call function with parameter " ^ var))
    | SimpleAssignmentExpression(var1,e) ->
        let v1,env = eval env e in
	   (match e with
	    Id(op,var) -> let (size,list)= StringHash.find env var in
		let rsize = fst (size,list) in
		   if List.length rsize != 0 then raise (Failure ("LHS is a number type but RHS is not a number type")) else 
	       (StringHash.replace env var1 ([],[v1]))
	  | _ -> (StringHash.replace env var1 ([],[v1]))); v1,env
    | ArraySingleInitialiseExpression(arr,list) ->
    	let dim = (match arr with 
    		ArrayExpression(size1,size2,var) -> 
    			(match (size1,size2) with
    				([Noexpr],[]) -> 1
    			      | ([Noexpr],[Noexpr]) -> 2
    			      | _ -> 0)
    	      | _ -> 0 ) in
    	if dim = 0 then raise (Failure ("Invalid Single Array Initialize Assignment")) else 
    	if dim = 1 then  
        let key, env = str env arr 0 in
             let vlist = List.map (fun a -> fst a) (List.map (eval env) list) in
                (StringHash.replace env key ([1;List.length vlist],vlist));0,env   
	else raise (Failure ("Invalid Array Assignment Expression: dimensions of LHS and RHS do not match"))
    | ArrayMultiInitialiseExpression(arr,list) ->
        let key, env = str env arr 0 in
		  let (size,list1) = StringHash.find env key in
			let csize =  List.hd (List.rev size) in
			let vlist2 = if csize<0 then raise (Failure ("Invalid Column Dimension ")) else List.map (List.map (eval env)) list in
				let vlist = List.map (List.map (fun a -> fst a)) vlist2 in 
					let _ = List.map (fun a -> if List.length a > csize then raise (Failure ("Column Dimension Mismatch In Initialization")) else 0) vlist in
					 let list2 = List.map (fun a -> a @ (Array.to_list (Array.make (if (List.length a<csize) then csize-(List.length a) else (List.length a) mod csize) 0))) vlist in
				  let templist = List.concat list2 in
                   let rsize = if csize==0 then 1 else ((List.length templist) / csize) in
					 (StringHash.replace env key ([rsize;csize],templist));0,env      
    | ArrayAssignmentExpression(arr,e1) ->
          (match e1 with 
              Minus(arr1) -> 
      	               let key, env = str env arr1 1 in
      	               let v = StringHash.mem env key in
      					if v then
       	                let (size,list1) = StringHash.find env key in
       	                match size with
           		    [] -> raise (Failure ("Invalid Minus expression: " ^ key ^ " is not an array"))
           		   | _ ->
                         let rlist = if StringHash.mem env "temp&" then (snd (StringHash.find env "temp&")) else [] in
                          let _ = StringHash.remove env "temp&" in
                           let list = if List.length rlist = 0 then list1 
                                      else 
                                        if List.hd size = 1 then (List.map (fun i -> List.nth list1 i) rlist) 
                                        else getSelectedRows rlist size list1 in 
                            let mkey, env = str env arr 0 in  
                              ( if List.hd size = 1 then (StringHash.replace env mkey ([1;List.length list],List.rev list)) 
                                else 
                                  let finallist =
                                   let rec reverse rlist tlist s e = 
                                    let temparr = Array.of_list rlist in
                                      let temp2 = Array.sub temparr s e in
                                        let jlist = (tlist @ Array.to_list temp2) in
                                         if s > 0 then reverse rlist jlist (s-e) e else jlist in 
                                           let revlist = [] in
                                            let eindex =  (List.hd (List.tl size)) in  
                                             let sindex = (((List.length list/eindex)-1)*(eindex)+0) in
                                    reverse list revlist sindex eindex in 
                               (StringHash.replace env mkey ([(List.length finallist)/(List.hd (List.tl size));List.hd (List.tl size)],finallist)))
           	 else raise (Failure ("Invalid Minus expression: undeclared indentifier " ^ key))
							
            | SetExpression(op,a1,a2) ->			
                 let key1, env = str env a1 1 in
				  let flag1 = StringHash.mem env key1 in
					let f1 = 
					(match a1 with
                       ArrayExpression(size1,size2,var) -> 
                         (match (size1,size2) with
						    ([Noexpr],[]) -> 1
                          | ([Noexpr],[Noexpr]) -> 2
                          | _ -> 0)
                       | _ -> 0
                     ) in
					let f2 = 
					(match a2 with
                       ArrayExpression(size1,size2,var) -> 
                         (match (size1,size2) with
                            ([Noexpr],[]) -> 1
                          | ([Noexpr],[Noexpr]) -> 2
                          | _ -> 0)
                       | _ -> 0)
                        in
					
					let (size1,list) = if flag1==false then raise (Failure ("Undeclared Identifier " ^ key1)) 
														else if (f1==1 && f2==2) || (f1==2 && f2==1) then raise (Failure ("Invalid Parameters in Set Operation")) 
														else
															StringHash.find env key1 in
                           let rlist = if StringHash.mem env "temp&" then (snd (StringHash.find env "temp&")) else [] in
                            let _ = StringHash.remove env "temp&" in
                            let list1 = if List.length rlist = 0 then list 
                                        else 
                                        if List.hd size1 = 1 then (List.map (fun i -> List.nth list i) rlist) 
                                        else getSelectedRows rlist size1 list in 
						let key2, env = str env a2 1 in							
              let flag2 = StringHash.mem env key2 in
					 let (size2,list) = if flag2==false then raise (Failure ("Undeclared Identifier " ^ key2))
															else StringHash.find env key2 in
                    	  	let rlist = if StringHash.mem env "temp&" then (snd (StringHash.find env "temp&")) else [] in
                       		 let _ = StringHash.remove env "temp&" in
                          let list2 = if List.length rlist = 0 then list 
                                      else 
                                        if List.hd size2 = 1 then (List.map (fun i -> List.nth list i) rlist) 
                                        else getSelectedRows rlist size2 list in 
					  let csize1 =  List.hd (List.rev size1) in
						let csize2 = List.hd (List.rev size2) in
						let rsize1 = List.hd size1 in
						 let rsize2 = List.hd size2 in	
                          let mkey, env = str env arr 0 in  
						  (match op with  
		      				Union -> if rsize1 == 1 && rsize2 == 1 then 
            							 let d = list1 @ list2 in
            							  let unionlist =
            								let rec lst d = 
            								 match d with 
            								  [] -> []
            							    | [x] -> [x]
            							    | x :: remainder -> if (List.mem x remainder) then lst (remainder) 
            							                        else [x] @ lst (remainder) in
            							    lst d in (StringHash.replace env mkey ([1;csize1],unionlist))
	    							  else 
										(if rsize1>1 && rsize2>1 then if csize1!=csize2 then raise (Failure ("No of columns should be same in union operation")) else(
										 let dim1 = (match a1 with
                       ArrayExpression(size1,size2,var) -> let v1,env = eval env (List.hd size2) in
																														if v1 != -1 then 1 else 0 
																										|_ -> -1)				in
												let dim2 =  (match a2 with
                       ArrayExpression(size1,size2,var) -> let v1,env = eval env (List.hd size2) in
																														if v1 != -1 then 1 else 0
																									|_-> -1					) in
												if dim1 == 1 || dim2 ==1 then raise (Failure ("Nothing should be specified in column index")) else
											 let totallist = list1 @ list2 in
											let ulist = 
											 let rec ftemp totallist =
											   if (List.length totallist)<(2*csize1) then [totallist] 
                                               else (let templst1 = Array.to_list (Array.sub (Array.of_list totallist) 0 csize1) in
												let x = [templst1] in
												 let z = Array.to_list (Array.sub (Array.of_list totallist) csize1 ((List.length totallist) - csize1))
                                                   in x @ ftemp z) in
										     ftemp totallist in 
											  let finallist = 
												let rec xyz tmp = 
												 (match tmp with
												     [] -> []
												   | [x] -> [x]
												   | x :: remainder -> if (List.mem x remainder) then xyz (remainder) 
												                       else [x] @ xyz (remainder)) in
												xyz ulist in (StringHash.replace env mkey ([(List.length finallist);csize1],(List.concat finallist))))						
										else (raise (Failure ("Union operation not possible between 1D and 2D arrays"))))
							
                           | Intersect ->  if rsize1 == 1 && rsize2 == 1 then
											  let intersectlist =
												let rec inter list1 list2=
												 match list1 with 
												   [] -> []
												 | [x] -> if (List.mem x list2) then [x] else []
												 | x :: remainder -> if (List.mem x list2) then [x] @ inter remainder list2
												                     else inter remainder list2 in
												inter list1 list2 in 
												let intersectfinallist =
            								let rec lst d = 
            								 match d with 
            								  [] -> []
            							    | [x] -> [x]
            							    | x :: remainder -> if (List.mem x remainder) then lst (remainder) 
            							                        else [x] @ lst (remainder) in
            							    lst intersectlist in 												
												(StringHash.replace env mkey ([1;csize1],intersectfinallist))							
											 else 
                       (if rsize1>1 && rsize2>1 then if csize1!=csize2 then raise (Failure ("No of columns should be same in intersect operation")) else(
											let dim1 = (match a1 with
                       ArrayExpression(size1,size2,var) -> let v1,env = eval env (List.hd size2) in
																														if v1 != -1 then 1 else 0 
																										|_ -> -1)				in
												let dim2 =  (match a2 with
                       ArrayExpression(size1,size2,var) -> let v1,env = eval env (List.hd size2) in
																														if v1 != -1 then 1 else 0
																									|_-> -1					) in
												if dim1 == 1 || dim2 ==1 then raise (Failure ("Nothing should be specified in column index")) else
													let ilist1 = 
												 let rec ftemp1 totallist1 =
												  if (List.length totallist1)<(2*csize1) then [totallist1] 
                                                  else (let templst1 = Array.to_list (Array.sub (Array.of_list totallist1) 0 csize1) in
													let x = [templst1] in
													 let z = Array.to_list(Array.sub(Array.of_list totallist1)csize1((List.length totallist1)-csize1))
                                                      in x @ ftemp1 z) in
												 ftemp1 list1 in
												  let ilist2 = 
													let rec ftemp2 totallist2 =
													 if (List.length totallist2)<(2*csize1) then [totallist2] 
                                                     else (let templst2 = Array.to_list (Array.sub (Array.of_list totallist2) 0 csize1) in
													  let x = [templst2] in
													   let z = Array.to_list(Array.sub(Array.of_list totallist2)csize1((List.length totallist2)-csize1))
                                                        in x @ ftemp2 z) in
													 ftemp2 list2 in		
													  let finallist = 
														let rec ixyz itmp1 itmp2 = 
														  match itmp1 with 
															[] -> []
														  | [x] -> if (List.mem x itmp2) then [x] else []
														  | x :: remainder -> if (List.mem x itmp2) then [x] @ ixyz remainder itmp2
														                      else ixyz remainder itmp2 in
														ixyz ilist1 ilist2 in 
														let intersectfinallist = 
												let rec xyz tmp = 
												 (match tmp with
												     [] -> []
												   | [x] -> [x]
												   | x :: remainder -> if (List.mem x remainder) then xyz (remainder) 
												                       else [x] @ xyz (remainder)) in
												xyz finallist in (StringHash.replace env mkey ([(List.length intersectfinallist);csize1],(List.concat intersectfinallist))))
												else (raise (Failure ("Intersect operation not possible between 1D and 2D arrays"))))
														
                            | Difference ->   if rsize1 == 1 && rsize2 == 1 then
												let difflist =
												 let rec diff list1 list2 =
												  match list1 with 
												   [] -> []
												 | [x] -> if (List.mem x list2) then [] else [x]
												 | x :: remainder -> if (List.mem x list2) then diff remainder list2
												                     else [x] @ diff remainder list2 in
												 diff list1 list2 in (StringHash.replace env mkey ([1;csize1],difflist))		
											  else 
                                              (if rsize1>1 && rsize2>1 then if csize1!=csize2 then raise (Failure ("No of columns should be same in difference operation")) else(
												let dim1 = (match a1 with
                       ArrayExpression(size1,size2,var) -> let v1,env = eval env (List.hd size2) in
																														if v1 != -1 then 1 else 0 
																										|_ -> -1)				in
												let dim2 =  (match a2 with
                       ArrayExpression(size1,size2,var) -> let v1,env = eval env (List.hd size2) in
																														if v1 != -1 then 1 else 0
																									|_-> -1					) in
												if dim1 == 1 || dim2 ==1 then raise (Failure ("Nothing should be specified in column index")) else
												let dlist1 = 
												 let rec ftemp1 totallist1 =
												  if (List.length totallist1)<(2*csize1) then [totallist1] 
                                                  else(let templst1 = Array.to_list (Array.sub (Array.of_list totallist1) 0 csize1) in
													let x = [templst1] in
													 let z = Array.to_list (Array.sub(Array.of_list totallist1)csize1((List.length totallist1)-csize1))
                                                      in x @ ftemp1 z) in
												 ftemp1 list1 in
												  let dlist2 = 
												   let rec ftemp2 totallist2 =
													if (List.length totallist2)<(2*csize1) then [totallist2] 
                                                    else(let templst2 = Array.to_list (Array.sub (Array.of_list totallist2) 0 csize1) in
													 let x = [templst2] in
													  let z = Array.to_list(Array.sub(Array.of_list totallist2)csize1((List.length totallist2)-csize1))
                                                       in x @ ftemp2 z) in
												   ftemp2 list2 in
													let finallist = 
													 let rec dxyz dtmp1 dtmp2 = 
													  match dtmp1 with 
														[] -> []
													  | [x] -> if (List.mem x dtmp2) then [] else [x]
													  | x :: remainder -> if (List.mem x dtmp2) then dxyz remainder dtmp2
													                      else [x] @ dxyz remainder dtmp2 in
													  dxyz dlist1 dlist2 in 
													   (StringHash.replace env mkey ([(List.length finallist);csize1],(List.concat finallist))))
                                             else (raise (Failure ("Difference operation not possible between 1D and 2D arrays")))))
               												
				      | ArrayExpression(size1,size2,var) -> (* let _ = print_string "here I am" in *)
                                         let rhsdim =
                                          if (StringHash.mem env var) then
                                            let rows = List.hd (fst (StringHash.find env var)) in
                                             (match rows with
                                                0 -> 0
                                              | 1 -> 1
                                              | _ -> 2  
                                             )
                                          else
                                           raise (Failure ("The Variable "  ^ var ^ " not found for array assignment " )) in
                                          let lhsdim = 
                                            (match arr with
                                               ArrayExpression(size3,size4,var2) -> if (StringHash.mem env var2) then 
                                                                                     let rows = List.hd (fst (StringHash.find env var2)) in
                                                                                      (match rows with
                                                                                           0 ->0
                                                                                         | 1 -> 1
                                                                                         | _ -> 2
                                                                                       )
                                                                                      else
                                                                                      (match (size3,size4) with
                                                                                            ([Noexpr],[Noexpr]) -> 2
						                                         |  ([Noexpr],[]) -> 1
                                                                                         |  ([Noexpr],hd :: tl) -> 2
		                                                                         | _    ->  raise (Failure ("Wrong expression on Lhs for array assignment."))
                                                                                       )
                                             |   _ -> raise (Failure ("Wrong expression on Lhs for array assignment."))
                                            ) in

                                            let test  =    ( match (lhsdim,rhsdim) with
                                                                 (1,2) -> 3
                                                               | (2,1) -> 4
                                                               | _  ->  -2
                                                          ) in
                                                   if test = 3 then
                                                      (match (size1,size2) with
                                                           ([Noexpr],y::[]) -> (match arr with
                                                                    ArrayExpression(size3,size4,var2) -> 
                                                                               let (size,list) = StringHash.find env var in
                                                                                let dm_y , _ = eval env y in 
                                                                                 let columns = List.hd(List.rev size) in
                                                                                 if dm_y >= columns || dm_y < 0 then 
                                                                                  raise (Failure ("Invalid arguments in array " ^ var ))
                                                                                 else 
                                                                                  let columnlist = 
                                                                                   let rec f lst n last =
                                                                                    if n >= last then
                                                                                     lst
                                                                                    else f ([(List.nth list n)]@lst) (n+columns) last
                                                                                   in f [] dm_y (List.length list)
                                                                                   in (StringHash.replace env var2 ([1]@[List.length columnlist],(List.rev columnlist)))
                                                               | _ -> raise(Failure ("Wrong expression on Lhs for array assignment."))
                                                             )
                                                         | (x::[],[Noexpr]) -> (match arr with
                                                                    ArrayExpression(size3,size4,var2) -> 
                                                                               let (size,list) = StringHash.find env var in
                                                                                let dm_x , _ = eval env x in 
                                                                                 let rows = List.hd size in
                                                                                 if dm_x >= rows || dm_x < 0 then 
                                                                                  raise (Failure ("Invalid arguments in array " ^ var ))
                                                                                 else 
                                                                                  let rowlist = 
                                                                                   let rec f lst n last =
                                                                                   if n >= last then
                                                                                     lst
                                                                                   else f ([(List.nth list n)]@lst) (n+1) last
                                                                                     in f [] (dm_x*(List.hd(List.rev size))) ((dm_x+1)*(List.hd(List.rev size)))
                                                                                      in (StringHash.replace env var2 ([1]@[List.length rowlist],(List.rev rowlist)))
                                                               | _ -> raise(Failure ("Wrong expression on Lhs for array assignment."))
                                                             )
                                                         |  _  ->  raise(Failure ("Wrong expression on Rhs for array assignment."))
                                                       )
                                                     else if test = 4 then
                                                       (match arr with
                                                           ArrayExpression(size3,size4,var2) -> 
                                                            if StringHash.mem env var2 then
                                                             (match (size3,size4) with
                                                                ([Noexpr],y::[]) -> 
                                                                               let (size,list) = StringHash.find env var2 in
                                                                                let dm_y , _ = eval env y in 
                                                                                 let rows = List.hd size in
                                                                                 let columns = List.hd (List.rev size) in
                                                                                 if dm_y >= columns || dm_y < 0 then 
                                                                                  raise (Failure ("Invalid arguments in array " ^ var2 ))
                                                                                 else
                                                                                  let (sz,lst) = StringHash.find env var in
                                                                                  let colR = List.hd (List.rev sz) in
                                                                                   if colR = rows then
                                                                                     let arr1 = Array.of_list list in
                                                                                      let rec f ar n step ls = 
                                                                                        (match ls with
                                                                                            []   -> ar
                                                                                          |  hd::tl -> let _ = ar.(n) <- hd in f ar (n+step) step  tl 
                                                                                        )
                                                                                       in let _ = f arr1 dm_y columns lst in
                                                                                       let newlst = Array.to_list arr1 in
                                                                                            StringHash.replace env var2 (size,newlst)
                                                                                   else 
                                                                                    raise (Failure ("Wrong expression on Lhs for array assignment."))
                                                               | (x::[],[Noexpr]) -> 
                                                                               let (size,list) = StringHash.find env var2 in
                                                                                let dm_y = List.hd (List.rev(size)) in
                                                                                let dm_x , _ = eval env x in 
                                                                                 let rows = List.hd size in
                                                                                 if dm_x >= rows || dm_x < 0 then 
                                                                                  raise (Failure ("Invalid arguments in array " ^ var2 ))
                                                                                 else
                                                                                  let (sz,lst) = StringHash.find env var in
                                                                                  let colR = List.hd (List.rev sz) in
                                                                                   if colR = dm_y then
                                                                                     let arr1 = Array.of_list list in
                                                                                      let rec f ar n ls = 
                                                                                        (match ls with
                                                                                            []   -> ar
                                                                                          |  hd::tl -> let _ = ar.(n) <- hd in f ar (n+1) tl 
                                                                                        )
                                                                                       in let _ = f arr1 (dm_x*colR) lst in
                                                                                       let newlst = Array.to_list arr1 in
                                                                                            StringHash.replace env var2 (size,newlst)
                                                                                   else 
                                                                                    raise (Failure ("Wrong expression on Lhs for array assignment."))
                                                               |  _  -> raise (Failure ("Wrong expression on Lhs for array assignment."))
                                                              )
                                                             else
                                                              raise (Failure ("Array expression on Lhs for array assignment is not defined"))
                                                          | _ -> raise (Failure ("Wrong expression on Lhs for array assignment."))
                                                       )
                                                              
                                                     else

											(let arrdim_used = 
		                       (match e1 with
		                         ArrayExpression(size1,size2,var) -> 
		                         (match (size1,size2) with
					    								([Noexpr],[Noexpr]) -> 22
															| ([Noexpr],_) -> 1
															| (_,[Noexpr]) -> 2
		                          | _ -> let v1 = List.length size2 in
																														if v1 != 1 then 1 else 0  )
		                       | _ -> 0
		                       ) in
											if arrdim_used!=0 then
				                     let key, env = str env e1 1 in
                      	let (size,list) = StringHash.find env key in
                        let rlist = if StringHash.mem env "temp&" then (snd (StringHash.find env "temp&")) else [] in
                          let _ = StringHash.remove env "temp&" in
                            let mlist = if List.length rlist = 0 then list 
                                        else 
                                        if List.hd size = 1 then (List.map (fun i -> List.nth list i) rlist) 
                                        else getSelectedRows rlist size list in 
                             let mkey, env = str env arr 0 in 
                               let eindex = List.hd (List.tl size) in
                                  if List.hd size = 1 then (StringHash.replace env mkey ([1;List.length mlist],mlist))
                                  else (StringHash.replace env mkey ([List.length mlist/eindex;eindex],mlist))
		                  else
														let value,env = eval env e1 in  
		                     			(match arr with 
		                      			 ArrayExpression(ex1,ex2,var) ->
		                       			  let (size,list) = StringHash.find env var in
		                        	     let arr = Array.of_list list in
		                         				let v1,env = eval env (List.hd ex1) in
		                         					if List.length ex2 = 0 then
		                         					  Array.set arr v1 value
		                         				  else
		                          				(let v2,env = eval env (List.hd ex2) in 
		                           	      let cindex = List.hd (List.rev size) in
		                                   Array .set arr ((v1*cindex)+v2) value);
									                       let list1 = Array.to_list arr in
									                         (StringHash.replace env var (size,list1))
		               													| _ -> ()) )
											| _ -> let value,env = eval env e1 in  
		                     			(match arr with 
		                      			 ArrayExpression(ex1,ex2,var) ->
		                       			  let (size,list) = StringHash.find env var in
		                        	     let arr = Array.of_list list in
		                         				let v1,env = eval env (List.hd ex1) in
		                         					if List.length ex2 = 0 then
		                         					  Array.set arr v1 value
		                         				  else
		                          				(let v2,env = eval env (List.hd ex2) in 
		                           	      let cindex = List.hd (List.rev size) in
		                                   Array .set arr ((v1*cindex)+v2) value);
									                       let list1 = Array.to_list arr in
									                         (StringHash.replace env var (size,list1))
		               													| _ -> ()) 
		                
           );0,env 
    
     | HashExpression(arr) ->
	let key, env = str env arr 1 in
	let v = StringHash.mem env key in
         if v then
      	  let (size,list) = StringHash.find env key in
      		  match size with
               [] -> raise (Failure ("Invalid Hash expression: " ^ key ^ " is not an array"))
              | _ ->
		let length = 
		(match arr with
                       ArrayExpression(size1,size2,var) -> 
                         (match (size1,size2) with
						    ([Noexpr],[]) -> if ((List.hd size) == 1) then List.length list 
						       		         else ((List.length list)/(List.hd (List.rev size)))
                          | ([Noexpr],[Noexpr]) -> List.length list
                          | _ -> 0  )
                      | _ -> 0
                      )in length, env
        else raise (Failure ("Invalid Hash expression: cannot find array " ^ key))   	               
    | PlusExpression(arr) ->
        let key, env = str env arr 1 in
         let v = StringHash.mem env key in
         if v then
          let dim = (match arr with
                        ArrayExpression(size1,size2,var) -> 
                                   (match size2 with
			                 [] -> 1
                                       | _  -> 2 )
                      | _ -> raise (Failure ("Invalid expression send in Plus expression"))   	
                    ) in
         if dim = 2 then
                 (match arr with
                        ArrayExpression(size1,size2,var) -> 
                                   (match (size1, size2) with
			                 ([Noexpr],[Noexpr]) -> let (size,list) = StringHash.find env key in
                                                                 let sum = List.fold_left (fun f s -> f + s) 0 list in sum, env
                                       | ( x :: [],[Noexpr])  -> let (size,list) = StringHash.find env key in
                                                                  let dm_x , _ = eval env x in 
                                                                  let rows = List.hd size in
                                                                   if dm_x >= rows || dm_x < 0 then 
                                                                     raise (Failure ("Invalid arguments in array " ^ var ))
                                                                   else 
                                                                     let rowlist = 
                                                                      let rec f lst n last =
                                                                        if n >= last then
                                                                         lst
                                                                        else f ([(List.nth list n)]@lst) (n+1) last
                                                                     in f [] (dm_x*(List.hd(List.rev size))) ((dm_x+1)*(List.hd(List.rev size)))
                                                                     in let sum = List.fold_left (fun f s -> f + s) 0 rowlist in sum, env
                                       | ([Noexpr],y::[]) ->  let (size,list) = StringHash.find env key in
                                                                  let dm_y , _ = eval env y in 
                                                                  let columns = List.hd(List.rev size) in
                                                                   if dm_y >= columns || dm_y < 0 then 
                                                                     raise (Failure ("Invalid arguments in array " ^ var ))
                                                                   else 
                                                                     let columnlist = 
                                                                      let rec f lst n last =
                                                                        if n >= last then
                                                                         lst
                                                                        else f ([(List.nth list n)]@lst) (n+columns) last
                                                                     in f [] dm_y (List.length list)
                                                                     in let sum = List.fold_left (fun f s -> f + s) 0 columnlist in sum, env
                                       |  _ -> raise (Failure ("Invalid expression send in Plus expression"))   	
                                   )
                      | _ -> raise (Failure ("Invalid expression sent in Plus expression"))   	
                    ) 
         else
          let (size,list) = StringHash.find env key in
           match size with
               [] -> raise (Failure ("Invalid Plus expression: " ^ key ^ " is not an array"))
             |  _ -> 
            let rlist = if StringHash.mem env "temp&" then (snd (StringHash.find env "temp&")) else [] in
              let _ = StringHash.remove env "temp&" in
                let mlist = if List.length rlist = 0 then list else (List.map (fun i -> List.nth list i) rlist) in 
                  let sum = List.fold_left (fun f s -> f + s) 0 mlist in sum, env 
         else raise (Failure ("Invalid Plus expression: cannot find array " ^ key))   	
    | Concatenate(array1, array2) -> 
       	let key1, env = str env array1 1 in
       	 let key2, env = str env array2 1 in
       	  let v1 = StringHash.mem env key1 in
       	  if v1 then
       	   let v2 = StringHash.mem env key2 in
       	   if v2 then
       	   let (size1, list1) = StringHash.find env key1 in
       	     let (size2, list2) = StringHash.find env key2 in
       	     	match (size1, size2) with
       		    [], _ -> raise (Failure ("Invalid Concatenate expression: " ^ key1 ^ " is not an array"))
       		   |_, [] -> raise (Failure ("Invalid Concatenate expression: " ^ key2 ^ " is not an array"))
      		   | _ ->
              let rlist = if StringHash.mem env "temp&" then (snd (StringHash.find env "temp&")) else [] in
                let _ = StringHash.remove env "temp&" in
                  let clist2 = if List.length rlist = 0 then list2 else (List.map (fun i -> List.nth list2 i) rlist) in 
       	           let concat = list1 @ clist2 in   
	                 let m,n = 
	                    if List.hd size1 = 1 && List.hd size2 = 1 then List.hd size1, List.hd (List.tl size1) + List.hd (List.tl size2)
	                    else 
	                    (if List.hd size1 != 1 && List.hd size2 != 1 && List.hd (List.tl size1) = List.hd (List.tl size2) then
	                        List.hd size1 + List.hd size2, List.hd (List.tl size1) 
                        else raise (Failure ("The dimensions of array " ^ key1 ^ " and array " ^ key2 ^ " do not match")) ) in    
	                 (StringHash.replace env key1 (m::[n], concat));0,env
	 else raise (Failure ("Invalid concatenation argument: cannot find "^ key2)) 
	 else raise (Failure ("Invalid concatenation argument: cannot find "^ key1)) 
    | ArrayNumber(e1,e2,name) -> (*(print_endline (string_of_int 123));*)
        let v1,env = eval env e1 in 
          let v2,env = eval env e2 in 
            let (size,list) = if StringHash.mem env name then StringHash.find env name else raise (Failure ("Undeclared Identifier " ^ name)) in 
              let arr = Array.of_list list in 
                 if v2 = -1 then arr.(v1),env
                 else 
                   let cindex = List.hd (List.rev size) in
                     arr.((v1*cindex)+v2),env
    | ArrayExpression(size1,size2,var) -> 
       let tlist1 = List.map (fun a -> fst a) (List.map (eval env) size1) in
        let tlist2 = List.map (fun a -> fst a) (List.map (eval env) size2) in
         if List.length tlist1 <= 1 && List.length tlist2 <= 1 then
          let (size,list) = StringHash.find env var in 
           let arr = Array.of_list list in 
            if List.length tlist2 = 0 then arr.(List.hd tlist1),env
            else 
            let cindex = List.hd (List.rev size) in
             arr.((List.hd tlist1*cindex)+List.hd tlist2),env 
         else 0,env
     | Insert(arrtype_e,el) ->
             let var, env = str env arrtype_e 1 in 
                 let list_var = List.map (fun e -> let v,_ = eval env e in v) el in
                  let len = List.length list_var in
                   let bl = StringHash.mem env var in 
                     if bl then
                      let rows = List.hd (fst (StringHash.find env var)) in
                       let arrdim_used = 
                       (match arrtype_e with
                         ArrayExpression(size1,size2,var) -> 
                         (match (size1,size2) with
			    ([Noexpr],[]) -> 1
                          | ([Noexpr],[Noexpr]) -> 2
                          | _ -> 0  )
                       | _ -> raise (Failure ("Invalid expression send in Plus expression"))
                       ) in 
                       let acc_dim = 
                       (match rows with
                         0 ->0
                       | 1 -> 1
                       | _ -> 2
                       ) in
                       if arrdim_used = acc_dim then
                        let column_width =  List.hd (List.tl (fst (StringHash.find env var))) in
                         let orig_list = snd (StringHash.find env var) in
                         (match rows with
                             1 -> StringHash.replace env var ([1]@[column_width+len],orig_list@list_var)
                           | _ -> let rows2add = len / column_width in
                                   let modu =  len mod column_width in
                                    if modu = 0 then
                                      StringHash.replace env var ([rows+rows2add]@[column_width],orig_list@list_var)
                                    else
                                       let padded_lst =  
                                         let rec f s el = 
                                          (match s with
                                            0 -> el
                                          | _ -> f (s-1) el@[0] ) in f (column_width - modu) list_var in
                                       StringHash.replace env var ([rows+rows2add+1]@[column_width],orig_list@padded_lst)
                          
                         )
                     else
                        raise (Failure ("Wrong array used in Insert expression."));0,env
                     else
                        raise (Failure ("Unknown array used in Insert expression."))
     | Delete(array1, e) ->
        let key1, env = str env array1 1 in
         let v1,env = eval env e in 
				let arrdim_used =
													(match array1 with
                         ArrayExpression(size1,size2,var) -> 
                         (match (size1,size2) with
			   									 ([Noexpr],[]) -> 1
                          | ([Noexpr],[Noexpr]) -> 2
                          | _ -> 0  )
                       | _ -> 0
                       ) in
				 if v1<=0 then
				 raise (Failure ("Invalid index for the element to be deleted"))
				  else(
          let (size1, list1) = if StringHash.mem env key1 then StringHash.find env key1 else raise (Failure ("Undeclared Identifier " ^ key1)) in 
					 if v1>List.length list1 then raise (Failure ("Invalid index for the element/row to be deleted"))
					 else( let csize1 =  List.hd (List.rev size1) in
		   			let rsize1 = List.hd size1 in
						 if rsize1==1 then
							let _=  if arrdim_used == 2 then raise (Failure ("Array is 1 Dimension but here used like 2 Dimension")) in
					   (let finallist = 
					    if v1==1 then List.tl list1
					     else if v1==(List.length list1) then List.rev (List.tl (List.rev list1))
					      else
                 let temparr = Array.of_list list1 in
           			  let temp2 = Array.sub temparr 0 (v1-1) in 
            			 let temp3 = Array.sub temparr v1 ((Array.length temparr) - v1) in
             			  let temp4 = Array.append temp2 temp3 in 
              			 Array.to_list temp4 in 
              		   (StringHash.replace env key1 ([1;(List.length finallist)], finallist)))
											else(
												let _=  if arrdim_used == 1 then raise (Failure ("Array is 2 Dimension but here used like 1 Dimension")) in
										 	 let finallist=
											  if v1==1 then Array.to_list(Array.sub (Array.of_list list1) (csize1*v1) ((List.length list1) - (csize1*v1)))
												 else
												  if v1==((List.length list1)/csize1) then Array.to_list (Array.sub (Array.of_list list1) 0 (csize1*(v1-1)))
													 else
													  let temparr = Array.of_list list1 in
                  					 let temp2 = Array.sub temparr 0 (csize1*(v1-1)) in
								  					  let temp3 = Array.sub temparr (csize1*v1) ((Array.length temparr) - (csize1*v1)) in
              							 	 let temp4 = Array.append temp2 temp3 in 
              								  Array.to_list temp4 in 
										 						 (StringHash.replace env key1 ([(rsize1-1);(csize1)], finallist)))));0, env
		
     | Range(x1,x2)->
           let k1, env = eval env x1 in
             let k2, env = eval env x2 in
              let xst = [k1;k2] in
	       if k1>k2 || k1<0 || k2<0 then raise (Failure ("Invalid parameters for range")) 
		else
                let list =     
                  let rec rnge xs =
                    (match xs with 
                      [] -> []
                    | [x] -> [x]
                    | x :: y -> if x < (List.hd y) then [x] @ rnge [x+1;(List.hd y)] 
                                else [(List.hd y)])
    in rnge xst in (StringHash.add env "rangelist&" ([List.length list],list));-2,env
     | _ -> 0,env
    
     and str env arr flag =
         match arr with ArrayExpression(size1,size2,var)-> 
           let tlist1 = List.map (fun a -> fst a) (List.map (eval env) size1)in
             let list1 = 
               let rec build1 xs =
                    (match xs with 
                      [] -> []
                    | x :: y -> if x = -2 then if flag = 1 then 
                                                 let lst = snd (StringHash.find env "rangelist&") in
                                                  let dim_fst = List.hd (fst (StringHash.find env var)) in
                                                  if dim_fst = 1 then
                                                  let dim_snd = List.hd (List.rev (fst (StringHash.find env var))) in 
                                                   let last = List.hd (List.rev lst) in
                                                    if last >= dim_snd then
                                                     raise (Failure ("Range exceeds the Array Dimentions"))
                                                    else (snd (StringHash.find env "rangelist&"))@ build1 y
                                                  else
                                                   let last = List.hd (List.rev lst) in
                                                    if last >= dim_fst then
                                                     raise (Failure ("Range exceeds the Array Dimentions"))
                                                    else (snd (StringHash.find env "rangelist&"))@ build1 y
                                                else (snd (StringHash.find env "rangelist&"))@ build1 y
                    else if x = -1 then 
                             []
                         else if flag = 1 then 
                               let dim_fst = List.hd (fst (StringHash.find env var)) in
                               if dim_fst = 1 then
                                let dim_snd = List.hd (List.rev (fst (StringHash.find env var))) in
                                if x >= dim_snd then
                                 raise (Failure ("Range exceeds the Array Dimentions"))
                                else [x]@build1 y
                               else
                                 if x >= dim_fst then
                                  raise (Failure ("Range exceeds the Array Dimentions"))
                                 else [x]@build1 y
                              else [x]@build1 y 
                    )  
               in build1 tlist1 in  let _ =(StringHash.remove env "rangelist&") in
             let tlist2 = List.map (fun a -> fst a) (List.map (eval env) size2) in
                let list2 = 
               let rec build2 xs =
                    (match xs with 
                      [] -> []
                    | x :: y -> if x = -2 then if flag = 1 then 
                                                 let lst = (snd (StringHash.find env "rangelist&")) in
                                                  let last = List.hd ( List.rev lst) in
                                                   let dim_snd = List.hd (List.rev (fst (StringHash.find env var))) in 
                                                   if last >= dim_snd then
                                                    raise (Failure ("Range exceeds the Array Dimentions"))
                                                   else (snd (StringHash.find env "rangelist&"))@ build2 y
                                               else (snd (StringHash.find env "rangelist&"))@ build2 y
                    else if x = -1 then 
                             []
                         else if flag = 1 then 
                               let dim_snd = List.hd (List.rev (fst (StringHash.find env var))) in
                               if x >= dim_snd then
                                raise (Failure ("Range exceeds the Array Dimentions"))
                               else [x]@build2 y 
                              else [x]@build2 y
                   )  
               in build2 tlist2 in let _ = StringHash.remove env "rangelist&" in
               if flag = 0 then
                let list1 = if List.length list1 = 0 then [0] else list1 in
                 let list2 = if List.length list2 = 0 then [0] else list2 in
                  (StringHash.add env var (list1@list2,[]))
               else
                 if List.length list1 >= 1 then (StringHash.add env "temp&" ([],list1)) 
                 else ()
              ;var,env
       | _ -> "",env;
                      
      and getSelectedRows rlist size list =
      (let rec rnge xs =
        (match xs with 
           [] -> []
         | [x] -> [x]
         | x :: y -> if x < (List.hd y) then [x] @ rnge [x+1;(List.hd y)] 
                     else [(List.hd y)]) in
        let mylist =
          let rec elements rlist tlist eindex = 
            match rlist with 
               [] -> []
             | x :: y ->  let tlist = tlist@rnge [x;x+eindex-1] in 
                       if List.length y = 0 then tlist 
                       else elements y tlist eindex in 
            let eindex = List.hd (List.tl size) in
          elements (List.map (fun a -> a * eindex) rlist) [] eindex in 
      (List.map (fun i -> List.nth list i) mylist)); 
         
  in
    let rec exec env = function
      Expression(e) -> let _, env = eval env e in env
    | Show(e) -> let v,env = eval env e in print_string (string_of_int v);print_string " "; env
    | ShowStr(str) -> print_string str; env
    | ShowList(list) ->
    	ignore (List.map (fun i -> exec env i) list); env
    | For(assign,cond,loop,stmtlist) ->
       let v1,env = eval env assign in 
        while(let c,env = eval env cond in c = 1) do
          ignore (List.fold_left exec env stmtlist);
          let _,_ = eval env loop in ()
          done; env
    | If(con,stm1,stm2) ->
        let v, env = eval env con in
	      if v = 1 then List.fold_left exec env stm1 else List.fold_left exec env stm2;
	      | FunctionDeclaration (name, el,stml,e) -> StringHash.add ftable name (el,stml,e);env
    | FunctionSimpleAssignment(id,fcall) ->  let _ = exec env fcall in
                                              if (StringHash.mem env "return") then  
                                                 let size_l, val_l = StringHash.find env "return" in 
                                                       (match size_l with
                                                         [] ->  let test1 = StringHash.mem env id in 
                                                                 if test1 then
                                                                  let _ = StringHash.replace env id (size_l, val_l) in StringHash.remove env "return" ; env
                                                                 else
                                                                  let _ = StringHash.add env id (size_l, val_l) in StringHash.remove env "return"; env
                                                       | _  -> raise (Failure ("Wrong return Type"))
                                                     )
                                              else
                                               raise (Failure ("Function expected to return something ID"))
    | FunctionArrayAssignment(arrexpr,fcall) -> let _ = exec env fcall in
                                              let test = StringHash.mem env "return" in
                                               if test then
                                                let size_l, val_l = StringHash.find env "return" in 
                                                 let dimention_r =   
                                                  (match size_l with
                                                    [] -> 0
                                                  | hd::tail -> if (hd = 1) then 1 else 2
                                                  ) in
                                                 let size1,size2,var = (match arrexpr with
                                                                      ArrayExpression(size1,size2,var) -> size1,size2,var
                                                                   |  _ -> raise (Failure ("Unexpected Type"))
                                                                   ) in
                                                 let dimention_l = 
                                                  (match (size1,size2) with
                                                          ([Noexpr],[]) -> 1
                                                        | ([Noexpr],[Noexpr]) -> 2
                                                        |  _    -> raise (Failure ("lvalue not appropriate in function assignment"))
                                                  ) in
                                                 if (dimention_r = dimention_l) then
                                                   let _ = StringHash.replace env var (size_l, val_l) in StringHash.remove env "return" ; env
                                                 else
                                                  raise (Failure ("Wrong return Type"))
                                               else
                                                raise (Failure ("Function expected to return something ID"))
    | FunctionCall(name, el) -> let test = StringHash.mem ftable name in
                                  if test then
                                   let formal_l,stm_l,return = StringHash.find ftable name in
                                    if List.length formal_l = List.length el then
                                     let temptable_formals = StringHash.create 50 in
                                      (*let _ = List.fold_left (fun env e -> let _, env = eval env e in env) env el in*)
                                        let isarraynumber e = 
                                         (match e with
			                   ArrayExpression(size1,size2,var) ->  
                                            (match (size1,size2) with
                                               (hd::[],[]) -> 
                                                ( match hd with 
                                                   Range(e1,e2) ->  0, Noexpr
                                                 | Noexpr -> 0,Noexpr
                                                 | _ -> 1, ArrayNumber(hd,Noexpr,var)
                                                )
                                            | (hd::[],hd1::[]) -> 
                                              ( match (hd,hd1) with 
                                                 (Range(e1,e2), _ ) ->  0,Noexpr
                                               | ( _ ,Range(e3,e4)) ->  0,Noexpr
                                               | ( _ ,Noexpr) ->  0,Noexpr
                                               | ( Noexpr ,_ ) ->  0,Noexpr
                                               | _          -> 1, ArrayNumber(hd,hd1,var)
                                              )
                                         | _ -> 0,Noexpr
                                        )

                                        | _ -> 0,Noexpr
                                       )
                                         in
let _ = List.iter2 (fun fe ae -> 
(match fe with
Id(op,var) ->
	   let v = StringHash.mem temptable_formals var in
            if v then 
               raise (Failure ("Argument " ^ var ^ " Declared in function  " ^ name ^ " has more than one instance."))
            else
             let value = 1 in 
              let value1 = (match op with 
	         SAdd -> value 
               | SSub -> -value) in
               let value2 , _ = 
                 
                 (match ae with
                    Id(op,var1) -> eval env ae
                 |  Literal(op,x) -> eval env ae
                 |  ArithmeticExpression(e1, op, e2) -> 
                            (match (e1, e2) with
                                Minus(e), _ ->   raise (Failure ("Can not call function with parameters other than ***"))
                              | _ , Minus(e)->   raise (Failure ("Can not call function with parameters other than ***"))
                              | SetExpression(op,exp1,exp2) , _ -> raise (Failure ("Can not call function with parameters other than ***"))
                              | _, SetExpression(op,exp1,exp2) -> raise (Failure ("Can not call function with parameters other than ***"))
                              | ArrayExpression(size1,size2,var),ArrayExpression(size3,size4,var1) ->  
                                    let test1,arrexpr1 = isarraynumber e1 in
                                     let test2,arrexpr2 = isarraynumber e2 in
                                     ( match (test1, test2) with
                                         1,1 -> let expr = ArithmeticExpression( arrexpr1,op,arrexpr2) in
                                                      eval env expr
                                       |  _  -> raise (Failure ("Can not call function with parameters other than ***"))
                                     )
                              | ArrayExpression(size1,size2,var) ,  _ ->  
                                     let test,arrexpr = isarraynumber e1 in
                                     ( match test with
                                         1 -> let expr = ArithmeticExpression( arrexpr,op,e2) in
                                                      eval env expr
                                       | _ -> raise (Failure ("Can not call function with parameters other than ***"))
                                     )
                              | _, ArrayExpression(size1,size2,var)  ->  
                                     let test,arrexpr = isarraynumber e2 in
                                     ( match test with
                                         1 -> let expr = ArithmeticExpression( e1,op,arrexpr) in
                                                      eval env expr
                                       | _ -> raise (Failure ("Can not call function with parameters other than ***"))
                                     )
                              | _,_ ->  eval env ae
                             )
                 |  HashExpression(arr) -> eval env ae
                 |  PlusExpression(arr) -> eval env ae
                 |  ArrayNumber(e1,e2,name) -> eval env ae
                 |  ArrayExpression(size1,size2,var) ->  
                                   let test,arrexpr = isarraynumber ae in
                                    if test = 1 then
                                     eval env arrexpr
                                    else
                                     raise (Failure ("Can not call function with parameters other than ***"))
                 |  _  -> raise (Failure ("Can not define function with parameters other than ***"))                   
               )
 in
                 (*print_endline(string_of_int value2)*) StringHash.add temptable_formals var ([],[value1*value2])  
|  ArrayExpression(size1,size2,var_f)->
           let v = StringHash.mem temptable_formals var_f in  
            if v then 
               raise (Failure ("Argument " ^ var_f ^ " Declared in function " ^ name ^ " has more than one instance."))
            else
             let dimention_f =
              ( match (size1,size2) with
                  ([Noexpr],[]) -> 1
                | ([Noexpr],[Noexpr]) -> 2
                |  _    -> raise (Failure ("Wrong argument type in function declaration " ^ name))
              ) in 
               let dimention_a =
                ( match ae with 
                   ArrayExpression(size1, size2,var) -> let rows = List.hd(fst (StringHash.find env var)) in
                                                        (match rows with
                                                           0 -> 0
                                                         | 1 -> 1
                                                         | _ -> 2
                                                         )  
                 | Minus(ArrayExpression(size1,size2,var)) ->  let rows = List.hd(fst (StringHash.find env var)) in
                                                        (match rows with
                                                           0 -> 0
                                                         | 1 -> 1
                                                         | _ -> 2
                                                         )  
                 | SetExpression(op,ArrayExpression(size1,size2,var1),ArrayExpression(size3,size4,var2)) ->  
                                              if(List.hd(fst (StringHash.find env var1)) = List.hd(fst (StringHash.find env var2))) then
                                                  let rows = List.hd(fst (StringHash.find env var1)) in
                                                        (match rows with
                                                           0 -> 0
                                                         | 1 -> 1
                                                         | _ -> 2
                                                         )  
                                                else
                                                   raise (Failure ("Different Array Size for setoperation arguments in function Call " ^ name))
                 | _ ->  raise (Failure ("Wrong argument type in function Call " ^ name))
                ) in
                  if dimention_f = dimention_a then
                    ( match ae with 
                       ArrayExpression(size1, size2,var) -> StringHash.add temptable_formals var_f (StringHash.find env var)
                     | Minus(ArrayExpression(size1,size2,var)) -> let _ =  StringHash.add temptable_formals var_f (StringHash.find env var) in
                                                                          let e = Minus(ArrayExpression(size1,size2,var_f)) in
                                                                           let _ = eval temptable_formals  e in ();
                     | SetExpression(op,ArrayExpression(size1,size2,var1),ArrayExpression(size3,size4,var2)) ->  
                                                           let _ =  StringHash.add temptable_formals var_f (StringHash.find env var1) in
                                                            let _ =  StringHash.add temptable_formals var2 (StringHash.find env var2) in
                                                             let e =  ArrayAssignmentExpression(ArrayExpression(size1,size2,var_f) ,SetExpression(op,ArrayExpression(size1,size2,var_f),ArrayExpression(size3,size4,var2))) in
                                                              let _ = eval temptable_formals  e in 
                                                                StringHash.remove temptable_formals var2 
                     | _ ->   raise (Failure ("Wrong argument type in function Call " ^ name))
                    )   
                  else
                      let _ = print_string "hello " in  raise (Failure ("Wrong argument type in function declaration " ^ name)) 
| _ -> raise (Failure ("Can not define function with parameters other than ***\n")) )) formal_l el in
                                      let _ = List.fold_left exec temptable_formals stm_l in
                                       (match return with
                                           Id(op,var1) -> let val1 , _ = eval temptable_formals return in StringHash.add env "return" ([],[val1])
                                        |  Literal(op,x) -> let val1,_ = eval temptable_formals return in StringHash.add env "return" ([],[val1])
                                        |  ArithmeticExpression(e1, op, e2) -> 
                                               (match (e1, e2) with
                                                  Minus(e), _ ->   raise (Failure ("Wrong Parameters to an arithmetic expression."))
                                                | _ , Minus(e)->   raise (Failure ("Wrong Parameters to an arithmetic expression."))
                                                | SetExpression(op,exp1,exp2) , _ -> raise (Failure ("Wrong Parameters to an arithmetic expression."))
                                                | _, SetExpression(op,exp1,exp2) -> raise (Failure ("Wrong Parameters to an arithmetic expression."))
                                                | ArrayExpression(size1,size2,var),ArrayExpression(size3,size4,var1) ->  
                                                                  let test1,arrexpr1 = isarraynumber e1 in
                                                                   let test2,arrexpr2 = isarraynumber e2 in
                                                                   (match (test1, test2) with
                                                                       1,1 -> let expr = ArithmeticExpression( arrexpr1,op,arrexpr2) in
                                                                                 let val1,_ = eval temptable_formals expr in StringHash.add env "return" ([],[val1])
                                                                     |  _  -> raise (Failure ("Wrong Parameters to an arithmetic expression."))
                                                                   )
                                                | ArrayExpression(size1,size2,var) ,  _ ->  
                                                                  let test,arrexpr = isarraynumber e1 in
                                                                   ( match test with
                                                                          1 -> let expr = ArithmeticExpression( arrexpr,op,e2) in
                                                                                let val1,_ = eval temptable_formals expr in StringHash.add env "return" ([],[val1])
                                                                        | _ -> raise (Failure ("Wrong Parameters to an arithmetic expression."))
                                                                    )
                                               | _, ArrayExpression(size1,size2,var)  ->  
                                                                  let test,arrexpr = isarraynumber e2 in
                                                                   ( match test with
                                                                        1 -> let expr = ArithmeticExpression( e1,op,arrexpr) in
                                                                              let val1,_ = eval temptable_formals expr in StringHash.add env "return" ([],[val1])
                                                                      | _ -> raise (Failure ("Wrong Parameters to an arithmetic expression."))
                                                                   )
                                              | _,_ ->  let val1,_ = eval temptable_formals return in StringHash.add env "return" ([],[val1])
                                             )
                                      |  HashExpression(arr) -> let val1,_ = eval temptable_formals return in StringHash.add env "return" ([],[val1])
                                      |  PlusExpression(arr) -> let val1,_ = eval temptable_formals return in StringHash.add env "return" ([],[val1])
                                      |  ArrayNumber(e1,e2,name) -> let val1,_ = eval temptable_formals return in StringHash.add env "return" ([],[val1])
                                      |  ArrayExpression(size1,size2,var) ->  
                                                     let test,arrexpr = isarraynumber return in
                                                            if test = 1 then
                                                             let val1,_ = eval temptable_formals return in StringHash.add env "return" ([],[val1])
                                                            else
                                                             let _ = StringHash.add temptable_formals "return" (StringHash.find temptable_formals var) in
                                                              let e =  ArrayAssignmentExpression(ArrayExpression(size1,size2,"return") ,return) in
                                                               let _= eval temptable_formals  e in
                                                                 StringHash.add env "return" (StringHash.find temptable_formals "return")
                                      | Minus(ArrayExpression(size1,size2,var)) -> let _ = eval temptable_formals  return in 
                                                                 StringHash.add env "return" (StringHash.find temptable_formals var)
                                      | SetExpression(op,ArrayExpression(size1,size2,var1),ArrayExpression(size3,size4,var2)) ->
                                                             let e =  ArrayAssignmentExpression(ArrayExpression(size1,size2,var1), return) in
                                                              let _ = eval temptable_formals  e in 
                                                               StringHash.add env "return" (StringHash.find temptable_formals var1)
                                      |  _  -> ();                   
                                     )
                                       
                                     else
                                      raise (Failure ("Wrong Number of arguments in function Call to function " ^ name)); env 
                                   else
                                     raise (Failure ("Function called must be declared first " ^ name))
  in
    List.fold_left exec stable stmlist
  in 
    let stable = StringHash.create 50 in
       call stmlist stable
